Skip to content

Add optional Claude Code hook for blocking malicious packages#168

Closed
David Larsen (dc-larsen) wants to merge 3 commits intomainfrom
add-claude-code-hook
Closed

Add optional Claude Code hook for blocking malicious packages#168
David Larsen (dc-larsen) wants to merge 3 commits intomainfrom
add-claude-code-hook

Conversation

@dc-larsen
Copy link
Copy Markdown
Contributor

Summary

Adds a PreToolUse hook (hooks/socket-gate.ts) for Claude Code that intercepts package install commands and checks them against the Socket API before allowing installation.

  • Blocks packages with critical alerts (typosquats like browserlist, malware)
  • Blocks packages with high severity supply chain risks
  • Allows everything else through silently
  • Fails open on all errors (network, auth, parse) so it never disrupts legitimate work
  • Uses the same SOCKET_API_KEY env var as the MCP server
  • No additional dependencies (standalone Node.js script using --experimental-strip-types)

Files

File Description
hooks/socket-gate.ts The hook script
hooks/socket-gate.test.ts 13 tests (6 unit, 7 integration)
README.md Setup and usage docs

How it works

The hook reads Claude Code's PreToolUse stdin payload, extracts the package name from install commands (npm install, yarn add, bun add, pnpm add), calls the Socket /v0/purl endpoint with alerts=true, and returns deny if critical/high alerts are found.

Test results

▶ socket-gate hook
  ✔ allows non-Bash tools
  ✔ allows non-install commands
  ✔ allows lockfile-only installs
  ✔ allows empty input
  ✔ allows invalid JSON
  ✔ allows when no API key is set
  ✔ allows safe package (lodash)
  ✔ allows safe scoped package (@types/node)
  ✔ blocks typosquat (browserlist)
  ✔ handles versioned install
  ✔ handles pnpm add
  ✔ handles bun add
ℹ tests 13 | pass 13 | fail 0

Inspired by Jimmy Vo's blog post.

Adds a PreToolUse hook (socket-gate.ts) that intercepts npm/yarn/bun/pnpm
install commands and checks packages against the Socket API. Blocks packages
with critical or high severity alerts (typosquats, malware, supply chain
attacks). Fails open on all errors.

Includes tests and README documentation.
Uses 'socket package score' instead of calling the /v0/purl endpoint
directly. Auth is handled by the CLI's own config (socket login), so
no SOCKET_API_KEY env var is required.
Comment thread README.md Outdated
|----------------|----------|---------|
| **Critical** | Block installation | `browserlist` (typosquat of `browserslist`) |
| **High** | Block installation | Packages with known supply chain risks |
| **Low/None** | Allow | `express`, `lodash`, `react` |
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
| **Low/None** | Allow | `express`, `lodash`, `react` |
| **Medium/Low** | Allow | `express`, `lodash`, `react` |

No such thing as "None" severity

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in f5a8127. We rewrote the table around supply chain score bands rather than alert severity, so the Low/None row is gone.

Copy link
Copy Markdown
Member

@staltz André Staltz (staltz) left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if this hook will catch all the package installs. For example, what if Claude wrote new packages in the package.json and then ran npm install in bash? It wouldn't detect any new package in that case.

I don't know what's the best way of hooking in that case, because we don't know exactly what package was added, but I just want to leave this here. Perhaps this warrants a little warning in the README.md so users know how safe/unsafe this hook is.

Comment thread hooks/socket-gate.ts Outdated

if (!isSocketInstalled()) {
// CLI not installed, fail open
outputAllow()
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think if users installed this hook but forgot to install the CLI, they would like to be warned so they don't feel "safe" while in reality being totally exposed. So I'd use outputDeny here. It shouldn't be too disruptive because there's no reason to have the hook installed if you have no Socket CLI.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call. Dropped the Socket CLI entirely in f5a8127 and went back to calling /v0/purl directly with SOCKET_API_KEY. Applied the same fail-closed philosophy to the missing-key case: if SOCKET_API_KEY isn't set, the hook denies with a setup message instead of silently allowing.

- Drop Socket CLI dependency; call /v0/purl directly with SOCKET_API_KEY
- Block when score.supplyChain < 0.2 (typosquats, known malware)
- Deny when SOCKET_API_KEY is missing so users are not silently unprotected
- Fail open on network, parse, and timeout errors
- Add limitations section to README (manifest-edit gap, JS-only scope)
- Update tests to match new API-based flow
@dc-larsen
Copy link
Copy Markdown
Contributor Author

André Staltz (@staltz) addressed the coverage gap in f5a8127 — added a Limitations section to the README that calls out the manifest-edit + bare-install case explicitly, along with JS-only scope, direct downloads, and transitive/post-install gaps. Also points readers at the MCP server + Socket Firewall for defense in depth.

Other changes in the same commit:

  • Dropped the Socket CLI dependency and went back to calling /v0/purl directly with SOCKET_API_KEY
  • Simplified the block rule: score.supplyChain < 0.2 denies, everything else allows (still catches browserlist at 0.15)
  • Missing SOCKET_API_KEY now fails closed with a setup message, per your earlier feedback

@dc-larsen
Copy link
Copy Markdown
Contributor Author

Superseded by #172, which uses the public Socket MCP server at https://mcp.socket.dev/ (no API key, no CLI required) and adds support for PyPI, Cargo, RubyGems, Go, and NuGet in addition to npm.

@dc-larsen David Larsen (dc-larsen) deleted the add-claude-code-hook branch April 22, 2026 22:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants